// Author: Ivan Chau
package uk.ac.cam.kcic2.math;

public class Vector3D{

	public final double x;
	public final double y;
	public final double z;

	public Vector3D(double xVal, double yVal, double zVal){
		x = xVal;
		y = yVal;
		z = zVal;
	}

	public Vector3D(double[] values){
		assert values.length == Params.VECTOR3D_DIM;
		
		x = values[Params.X];
		y = values[Params.Y];
		z = values[Params.Z];
	}

	public Vector3D(Vector3DHomo homoVec){
		assert homoVec.w > Params.EPILSON;
		
		x = homoVec.x/homoVec.w;
		y = homoVec.y/homoVec.w;
		z = homoVec.z/homoVec.w;
	}

	public double get(int index){
		assert index >= Params.X && index <= Params.Z;
		
		switch (index){
		case Params.X: return x;
		case Params.Y: return y;
		case Params.Z: return z;
		default: System.err.println(index);throw new IndexOutOfBoundsException();
		}
	}
	
	public Vector3D plus(Vector3D arg){
		double xVal = this.x + arg.x;
		double yVal = this.y + arg.y;
		double zVal = this.z + arg.z;			

		return new Vector3D(xVal, yVal, zVal);	
	}

	public Vector3D sub(Vector3D arg){
		double xVal = this.x - arg.x;
		double yVal = this.y - arg.y;
		double zVal = this.z - arg.z;			

		return new Vector3D(xVal, yVal, zVal);	
	}

	public double dot(Vector3D arg){
		double result = this.x*arg.x + this.y*arg.y + this.z*arg.z;				
		
		return result;
	}

	public Vector3D cross(Vector3D arg){
		double xVal = this.y*arg.z - this.z*arg.y;
		double yVal = this.z*arg.x - this.x*arg.z;
		double zVal = this.x*arg.y - this.y*arg.x;	
		Vector3D result = new Vector3D(xVal, yVal, zVal);
		
		assert result.dot(this) <= Params.EPILSON && result.dot(arg) <= Params.EPILSON;
		
		return result;
	}

	public Vector3D mult(Matrix4 matrix){return matrix.mult(this);}

	public Vector3D mult(double scalar){
		double xVal = this.x*scalar;
		double yVal = this.y*scalar;
		double zVal = this.z*scalar;			

		return new Vector3D(xVal, yVal, zVal);	
	}

	public Vector3D div(double scalar){
		assert scalar > Params.EPILSON;
		
		double xVal = this.x/scalar;
		double yVal = this.y/scalar;
		double zVal = this.z/scalar;			

		return new Vector3D(xVal, yVal, zVal);	
	}

	public double norm(){return this.dot(this);}
	
	public Vector3D normalize(){return this.div(Math.sqrt(this.norm()));}
}
